package models

import (
	"github.com/jinzhu/gorm"
	"math"

	"kop/libs/database"
	"kop/modules/captain/configuration"
	"kop/modules/captain/configuration/class"
	"kop/modules/captain/configuration/fetter"
	"kop/modules/captain/entity"
	"kop/modules/configuration/configuration/attribute"
	"kop/modules/configuration/configuration/attribute/attrtype"
	"kop/pb"

	guildC "kop/modules/guild/client"
	models2 "kop/modules/maid/models"
)

func Create(dB *gorm.DB, roleID, captainID int32) entity.Captain {

	var conf, _ = configuration.GetByCaptainID(captainID)
	var talentMap = make(map[int32]int32, len(conf.Talent))
	for _, talent := range conf.Talent {
		talentMap[talent] = 1
	}

	var captain = entity.Captain{
		RoleID:      roleID,
		CaptainID:   captainID,
		Level:       1,
		TalentMap:   talentMap,
		Skill2Level: 1,
		Skill3Level: 1,
		Ready:       true,
	}

	if err := dB.Create(&captain).Error; err != nil {
		panic(err)
	}

	return captain
}

func New(roleID int32) *C {

	return &C{
		roleID:   roleID,
		Captains: getCaptains(database.DB, roleID),
		skills:   nil,
		maids:    nil}
}

//
type C struct {
	roleID   int32
	Captains []*entity.Captain
	skills   []*pb.GuildSkill
	maids    []*pb.Maid
}

// proto.Marshal 前
//func (c Captains) ToEntities() []*entity.Captain {
//
//	return c
//}

func (c *C) getSkills() []*pb.GuildSkill {

	if c.skills == nil {
		c.skills = guildC.GetSkills(c.roleID)
	}

	return c.skills
}

func (c *C) getMaids() []*pb.Maid {

	if c.maids == nil {
		c.maids = models2.FriendMaids(database.DB, c.roleID)
	}

	return c.maids
}

func (c *C) ToPbCaptains(data []*entity.Captain) []*pb.Captain {

	var res []*pb.Captain

	var skills = c.getSkills()
	var maids = c.getMaids()

	if data == nil {
		data = c.Captains
	}

	var m = make(map[int32]*entity.Captain)
	for _, v := range c.GetByReady() {
		m[v.CaptainID] = v
	}

	for _, v := range data {
		res = append(res, BuildPbCaptain(v, skills, maids, m))
	}

	return res
}

func (c *C) GetEntity(captainID int32) *entity.Captain {

	for _, v := range c.Captains {
		if v.CaptainID == captainID {
			return v
		}
	}

	panic("can't find captain")
}

//func (c Captains) ToEntityMap() map[int32]*entity.Captain {
//
//	var res = make(map[int32]*entity.Captain, len(c))
//	for _, v := range c {
//		res[v.CaptainID] = v
//	}
//
//	return res
//}

func (c *C) GetByReady() []*entity.Captain {

	var res []*entity.Captain
	for _, v := range c.Captains {
		if v.Ready {
			res = append(res, v)
		}
	}

	return res
}

func (c *C) GetByRingUniqueID(ID int32) *entity.Captain {

	for _, v := range c.Captains {
		if v.RingUniqueID == ID {
			return v
		}
	}

	return nil
}

func (c *C) GetByMaidID(maidID int32) *entity.Captain {

	for _, v := range c.Captains {
		if v.MaidID == maidID {
			return v
		}
	}

	return nil
}

func (c *C) ToMessage(captainID int32) *pb.Captain {

	var data = c.GetEntity(captainID)

	var skills = c.getSkills()
	var maids = c.getMaids()

	var m = make(map[int32]*entity.Captain)
	for _, v := range c.GetByReady() {
		m[v.CaptainID] = v
	}

	return BuildPbCaptain(data, skills, maids, m)
}

//
func getCaptains(dB *gorm.DB, roleID int32) []*entity.Captain {

	var data []*entity.Captain
	if err := dB.Where(entity.Captain{RoleID: roleID}).Find(&data).Error; err != nil {
		panic(err)
	}

	return data
}

//func GetCaptainsByReady(dB *gorm.DB, roleID int32, guildSkills []*pb.GuildSkill) []*pb.Captain {
//
//	var captains []entity.Captain
//	if err := dB.Where("role_id = ? AND ready = ?", roleID, true).Find(&captains).Error; err != nil {
//		panic(err)
//	}
//
//	var result = make([]*pb.Captain, 0, len(captains))
//
//	for _, capEntity := range captains {
//		var captain = BuildPbCaptain(capEntity, guildSkills)
//		result = append(result, &captain)
//	}
//
//	return result
//}

func GetCaptain(dB *gorm.DB, roleID, captainID int32) (entity.Captain, error) {

	var data entity.Captain
	if err := dB.Where(entity.Captain{RoleID: roleID, CaptainID: captainID}).First(&data).Error; err == gorm.ErrRecordNotFound {
		return entity.Captain{}, err
	} else if err != nil {
		panic(err)
	}

	return data, nil
}

//func GetByCaptainID(dB *gorm.DB, roleID, captainID int32) (pb.Captain, error) {
//
//	var capEntity entity.Captain
//	if err := dB.First(&capEntity, "role_id = ? and captain_id = ?", roleID, captainID).Error; err == gorm.ErrRecordNotFound {
//		return pb.Captain{}, err
//	} else if err != nil {
//		panic(err)
//	}
//
//	return BuildPbCaptain(capEntity, nil, nil, nil), nil
//}

//func CountByLevel(dB *gorm.DB, roleID, level int32) int32 {
//
//	var n int32
//	if err := dB.Model(&entity.Captain{}).Where("role_id = ? AND level >= ?", roleID, level).Count(&n).Error; err != nil {
//		panic(err)
//	}
//
//	return n
//}

func CountByClass(dB *gorm.DB, roleID, class int32) int32 {

	var n int32

	if err := dB.Model(&entity.Captain{}).Where("role_id = ? AND \"class\" >= ?", roleID, class).Count(&n).Error; err != nil {
		panic(err)
	}

	return n
}

func Save(db *gorm.DB, data *entity.Captain) {

	if err := db.Save(data).Error; err != nil {
		panic(err)
	}
}

func SetBuildingID(dB *gorm.DB, roleID, captainID, buildingID int32) error {

	var capEntity = entity.Captain{
		RoleID:    roleID,
		CaptainID: captainID,
	}
	return dB.Model(&capEntity).Updates(map[string]interface{}{"building_id": buildingID}).Error
}

// maids 已结交的侍女
// captains 上阵的船长
func BuildPbCaptain(data *entity.Captain, guildSkills []*pb.GuildSkill, maids []*pb.Maid, captains map[int32]*entity.Captain) *pb.Captain {

	var roleID = data.RoleID

	var captain pb.Captain

	captain.CaptainID = data.CaptainID
	captain.Level = data.Level
	captain.Class = data.Class
	captain.Exp = data.Exp
	captain.BuildingID = data.BuildingID
	captain.RingUniqueID = data.RingUniqueID
	captain.MaidID = data.MaidID
	captain.Ready = data.Ready

	var captainConf, err = configuration.GetByCaptainID(captain.CaptainID)
	if err != nil {
		panic(err)
	}

	attrConf, err := attribute.GetByAttributeID(captainConf.AttributeID + captain.Level)
	if err != nil {
		panic(err)
	}

	captain.MoneyCost = attrConf.MoneyCost
	captain.Camp = captainConf.Camp
	captain.Sex = captainConf.Sex

	// 技能1
	setSkill1(&captain)

	// 技能2 技能3
	setSkill2(&captain, data)

	// 天赋属性加成
	setTalent(&captain, data)

	// 天赋属性加成
	var attr = *captain.Attribute

	// 公会技能
	if captain.Camp != 0 {

		if guildSkills == nil {
			guildSkills = guildC.GetSkills(data.RoleID)
		}

		for _, v := range guildSkills {
			if v.Camp == captain.Camp {
				captain.Attribute.COR += int32(float32(attr.COR) * v.Value)
			}
		}
	}

	// 船羁绊
	captain.Fetter1 = &pb.Fetter{ID: captainConf.FetterShip, Enable: fetter.GetFetter(captainConf.FetterShip).ShipID == data.BuildingID}
	// 侍女羁绊
	if captainConf.FetterMaid != 0 {

		if maids == nil {
			maids = models2.FriendMaids(database.DB, roleID)
		}

		captain.Fetter2 = &pb.Fetter{ID: captainConf.FetterMaid}

		var fetterMaidID = fetter.GetFetter(captainConf.FetterMaid).MaidID

		for _, maid := range maids {
			if maid.MaidID == fetterMaidID {
				captain.Fetter2.Enable = true
				captain.Attribute.INT += int32(float64(attr.INT)*float64(maid.AttributeRate.INTRate)) + maid.Attribute.INT
				captain.Attribute.COR += int32(float64(attr.COR)*float64(maid.AttributeRate.CORRate)) + maid.Attribute.COR
				captain.Attribute.CHR += int32(float64(attr.CHR)*float64(maid.AttributeRate.CHRRate)) + maid.Attribute.CHR
				break
			}
		}

	}
	// 船长羁绊
	captain.Fetter3 = make([]*pb.Fetter, 0, 5)
	for _, fetterID := range captainConf.FetterCaptains {
		var conf = fetter.GetFetter(fetterID)
		if conf.CaptainID1 != 0 || conf.CaptainID2 != 0 || conf.CaptainID3 != 0 {

			var pbFetter = pb.Fetter{ID: fetterID, Enable: true}

			if conf.CaptainID1 != 0 {
				if _, ok := captains[conf.CaptainID1]; !ok {
					pbFetter.Enable = false
				}
			}

			if conf.CaptainID2 != 0 {
				if _, ok := captains[conf.CaptainID2]; !ok {
					pbFetter.Enable = false
				}
			}

			if conf.CaptainID3 != 0 {
				if _, ok := captains[conf.CaptainID3]; !ok {
					pbFetter.Enable = false
				}
			}

			captain.Fetter3 = append(captain.Fetter3, &pbFetter)

			if pbFetter.Enable {

				var fetterAttr, err = attribute.GetByAttributeID(fetter.GetFetter(fetterID).AttributeID)
				if err != nil {
					panic(err)
				}

				captain.Attribute.INT += int32(float64(attr.INT)*float64(fetterAttr.INTRate)) + fetterAttr.INT
				captain.Attribute.COR += int32(float64(attr.COR)*float64(fetterAttr.CORRate)) + fetterAttr.COR
				captain.Attribute.CHR += int32(float64(attr.CHR)*float64(fetterAttr.CHRRate)) + fetterAttr.CHR
			}
		}
	}

	// 爵位加成
	setClass(&captain, attr)

	//// 侍女加成
	//setMaid(&captain, data, attr)

	// 魔戒加成
	setRing(&captain, data)

	captain.Attribute.COR += 50
	captain.Attribute.INT += 50
	captain.Attribute.CHR += 50
	captain.Power = int64(float64(captain.Attribute.COR) / 100 * float64(captain.Attribute.INT))
	//captain.Power = int64(float64(captain.Attribute.COR)/1000*float64(captain.Attribute.INT)) + int64(captain.Attribute.COR+captain.Attribute.INT)
	return &captain
}

// 天赋属性加成
func setTalent(captain *pb.Captain, capEntity *entity.Captain) {

	var captainConf, err = configuration.GetByCaptainID(captain.CaptainID)
	if err != nil {
		panic(err)
	}

	attrConf, err := attribute.GetByAttributeID(captainConf.AttributeID + captain.Level)
	if err != nil {
		panic(err)
	}

	classConf, err := class.GetByCaptainClass(captain.Class)
	if err != nil {
		panic(err)
	}

	captain.Attribute = &pb.Attribute{INT: 0, COR: 0, CHR: 0}

	for _, talentID := range captainConf.Talent {

		var c, err = configuration.GetByTalentID(talentID)
		if err != nil {
			panic(err)
		}

		var level = capEntity.TalentMap[talentID]
		var value = c.Value * level

		switch c.Type {
		case attrtype.TypeINT:
			captain.Attribute.INT += value * attrConf.INT

		case attrtype.TypeCOR:
			captain.Attribute.COR += value * attrConf.COR

		case attrtype.TypeCHR:
			captain.Attribute.CHR += value * attrConf.CHR
		}

		captain.Talents = append(captain.Talents, &pb.Talent{
			TalentID:    talentID,
			Level:       level,
			ItemID:      c.ItemID,
			PieceNum:    c.PieceCount,
			Probability: c.Probability,
			MaxLevel:    classConf.TalentLevelMax,
		})
	}
}

// 技能1 船长主动技能
func setSkill1(captain *pb.Captain) {

	var captainConf, err = configuration.GetByCaptainID(captain.CaptainID)
	if err != nil {
		panic(err)
	}

	skill1Conf, err := configuration.GetBySkill1ID(captainConf.Skill1)
	if err != nil {
		panic(err)
	}

	skillTargetConf, err := configuration.GetBySkillTargetID(skill1Conf.TargetID)
	if err != nil {
		panic(err)
	}

	captain.Skill1 = &pb.CaptainSkill1{
		SkillID:       captainConf.Skill1,
		Level:         captain.Class + 1,
		Rate:          skill1Conf.Probability,
		TargetType:    skillTargetConf.TargetType, // 1我方 2敌方 3自身
		TargetNumber:  skillTargetConf.TargetCount,
		Value:         skill1Conf.ValueBase + float32(captain.Class)*skill1Conf.ValueInc,
		AttributeType: skill1Conf.AttributeType,
	}

}

// 技能2 技能3 船长被动技能
func setSkill2(captain *pb.Captain, capEntity *entity.Captain) {

	var captainConf, err = configuration.GetByCaptainID(captain.CaptainID)
	if err != nil {
		panic(err)
	}

	skill2Conf, err := configuration.GetBySkill2ID(captainConf.Skill2)
	if err != nil {
		panic(err)
	}

	attrConf, err := attribute.GetByAttributeID(skill2Conf.AttributeID + capEntity.Skill2Level)
	if err != nil {
		panic(err)
	}

	captain.Skill2 = &pb.CaptainSkill2{
		SkillID:   skill2Conf.SkillID,
		Level:     capEntity.Skill2Level,
		MaxLevel:  int32(math.Min(float64(skill2Conf.GradeMax), float64(capEntity.Level))),
		CRTRate:   attrConf.CRTRate,
		CRTDamage: attrConf.CRTDmgRate,
		ExpCost:   attrConf.ExpCost,
	}

	skill3Conf, err := configuration.GetBySkill2ID(captainConf.Skill3)
	if err != nil {
		panic(err)
	}

	attrConf, err = attribute.GetByAttributeID(skill3Conf.AttributeID + capEntity.Skill3Level)
	if err != nil {
		panic(err)
	}

	captain.Skill3 = &pb.CaptainSkill2{
		SkillID:   skill3Conf.SkillID,
		Level:     capEntity.Skill3Level,
		MaxLevel:  int32(math.Min(float64(skill3Conf.GradeMax), float64(capEntity.Level))),
		CRTRate:   attrConf.CRTRate,
		CRTDamage: attrConf.CRTDmgRate,
		ExpCost:   attrConf.ExpCost,
	}

}

// 爵位加成
func setClass(captain *pb.Captain, attr pb.Attribute) {

	classConf, err := class.GetByCaptainClass(captain.Class)
	if err != nil {
		panic(err)
	}

	attrConf, err := attribute.GetByAttributeID(classConf.AttributeID)
	if err != nil {
		panic(err)
	}

	captain.MaxLevel = classConf.CaptainLevelMax
	captain.ClassUpItems = classConf.Items

	// 爵位加成
	captain.Attribute.INT += int32(float64(attr.INT) * float64(attrConf.INTRate))
	captain.Attribute.COR += int32(float64(attr.COR) * float64(attrConf.CORRate))
	captain.Attribute.CHR += int32(float64(attr.CHR) * float64(attrConf.CHRRate))
}

// 侍女加成
//func setMaid(captain *pb.Captain, capEntity *entity.Captain, attr pb.Attribute) {
//
//	if capEntity.MaidID > 0 {
//		var maid pb.Maid
//		var err = proto.Unmarshal(capEntity.Maid, &maid)
//		if err == nil && maid.AttributeRate != nil {
//
//			// 侍女属性加成 = 船长属性 * 侍女比例加成 / 10000  + 侍女固定加成数值
//			captain.Attribute.INT += int32(float64(attr.INT)*float64(maid.AttributeRate.INTRate)) + maid.Attribute.INT
//			captain.Attribute.COR += int32(float64(attr.COR)*float64(maid.AttributeRate.CORRate)) + maid.Attribute.COR
//			captain.Attribute.CHR += int32(float64(attr.CHR)*float64(maid.AttributeRate.CHRRate)) + maid.Attribute.CHR
//		}
//	}
//}

// 魔戒加成
func setRing(captain *pb.Captain, capEntity *entity.Captain) {

	captain.RingAttribute = &pb.Attribute{
		INT: capEntity.RingINT,
		COR: capEntity.RingCOR,
		CHR: capEntity.RingCHR,
	}

	captain.Attribute.INT += captain.RingAttribute.INT
	captain.Attribute.COR += captain.RingAttribute.COR
	captain.Attribute.CHR += captain.RingAttribute.CHR
}
